package com.bwda.dsrs.systemservice.aspect;


import com.bwda.dsrs.base.centerbase.response.ResponseEntity;
import com.bwda.dsrs.base.util.CopyUtil;
import com.bwda.dsrs.base.util.UUIDUtil;
import com.bwda.dsrs.domain.po.adminlog.AdminLogPo;
import com.bwda.dsrs.systemservice.api.LogServiceApi;
import com.bwda.dsrs.systemservice.api.LogTemplate;
import com.bwda.dsrs.systemservice.domain.Constants;
import com.bwda.dsrs.systemservice.util.util.JwtTokenUtil;
import com.bwda.dsrs.systemservice.utils.IpAdrressUtil;
import com.bwda.dsrs.util.LogServiceUtil;
import org.apache.commons.lang3.StringUtils;
import org.apache.ibatis.javassist.ClassClassPath;
import org.apache.ibatis.javassist.ClassPool;
import org.apache.ibatis.javassist.CtMethod;
import org.apache.ibatis.javassist.bytecode.LocalVariableAttribute;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.*;
import org.aspectj.lang.reflect.MethodSignature;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import org.springframework.web.context.request.RequestAttributes;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;

import javax.annotation.Resource;
import javax.servlet.http.HttpServletRequest;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
import java.util.Collections;
import java.util.Date;
import java.util.HashMap;
import java.util.Map;

/**
 * 日志记录AOP实现
 *
 * @author yanyuemei
 * @date 2019 -04-29
 */
@Aspect
@Component
public class LogAspect {
    private static Logger logger = LoggerFactory.getLogger(LogAspect.class);
    @Resource
    private LogServiceApi logServiceApi;

    @Autowired
    private LogServiceUtil logServiceUtil;


    /**
     * 统一切点,对com.bwda.dsrs.systemservice.controller及其子包中所有的类的所有方法切面
     */
    @Pointcut("execution(public * com.bwda.dsrs.systemservice.controller..*.*(..))")
    public void pointcut() {
    }

    /**
     * 前置通知
     *
     * @param joinPoint join point
     */
    @Before("pointcut()")
    public void beforeMethod(JoinPoint joinPoint) {
        //logger.info("调用了前置通知");

    }

    /**
     * After method *
     *
     * @param joinPoint join point
     * @After: 后置通知
     */
    @After("pointcut()")
    public void afterMethod(JoinPoint joinPoint) {
        // logger.info("调用了后置通知");
    }

    /**
     * After returning method *
     *
     * @param joinPoint join point
     * @param result    result
     * @AfterRunning: 返回通知 rsult为返回内容
     */
    @AfterReturning(value = "pointcut()", returning = "result")
    public void afterReturningMethod(JoinPoint joinPoint, Object result) {
        try {
            // 获取request
            RequestAttributes requestAttributes = RequestContextHolder.getRequestAttributes();
            ServletRequestAttributes servletRequestAttributes = (ServletRequestAttributes) requestAttributes;
            HttpServletRequest request = null;
            if (servletRequestAttributes != null) {
                request = servletRequestAttributes.getRequest();
            }

            //目标方法实体
            Method method = ((MethodSignature) joinPoint.getSignature()).getMethod();
            //记录日志信息
            LogTemplate logTemplate = new LogTemplate();
            logTemplate.setServiceId("system");
            logTemplate.setId(1);
            logTemplate.setHandleType("toDB");
            AdminLogPo adminLogPo = new AdminLogPo();

            //方法注解实体
            Loggable methodLogAnnon = method.getAnnotation(Loggable.class);
            if (request != null) {
                //获取用户名
                String auth = request.getHeader(Constants.TOKEN_HEADER);
                if (StringUtils.isNotEmpty(auth)) {
                    String userCode = JwtTokenUtil.getUserCode(auth);
                    adminLogPo.setLoginToken(auth);
                    adminLogPo.setUserCode(userCode);
                    adminLogPo.setUserName(JwtTokenUtil.getUserName(auth));
                    adminLogPo.setOrgName(JwtTokenUtil.getOrgName(auth));
                }
                adminLogPo.setIpAddr(IpAdrressUtil.getIpAdrress(request));
                adminLogPo.setUserAgent(request.getHeader("user-agent"));
                adminLogPo.setRequestUri(request.getRequestURL().toString());
                adminLogPo.setCreateTime(new Date());
            }

            //获取请求的类名
            String className = joinPoint.getTarget().getClass().getName();

            //获取请求的方法名
            String methodName = method.getName();
            adminLogPo.setMethod(className + "." + methodName);
            //参数列表
            if (methodLogAnnon != null) {
                //操作描述
                String operateName = methodLogAnnon.descp().toString();
                adminLogPo.setOperateContent(operateName);
                //操作类型
                String operateType = methodLogAnnon.type().value().toString();
                adminLogPo.setOperateType(operateType);
            }

            if (result instanceof ResponseEntity) {
                ResponseEntity responseEntity = (ResponseEntity) result;
                if (responseEntity.getMsg() != null) {
                    adminLogPo.setExceptionMsg(responseEntity.getMsg());
                    //请求结果
                    adminLogPo.setStatus(responseEntity.getRescode());
                }
            } else {
                //请求结果
                adminLogPo.setStatus(Constants.RESULT_SUCCESS);
            }
            logTemplate.setAdminLogPo(adminLogPo);
            String uid = UUIDUtil.getUuid();
            logTemplate.setUid(uid);
            //调用日志服务
            com.bwda.dsrs.domain.po.serverslog.LogTemplate logTemplate1 = getConditionByQueryForm(logTemplate);
            logServiceUtil.handleLogServiceUtil(logTemplate1);

        } catch (Exception e) {
            e.printStackTrace();
            logger.info("处理管理系统日志信息失败！{}", e.getLocalizedMessage());
        }
    }

    /**
     * After returning method *
     *
     * @param joinPoint join point
     * @param e         e
     * @AfterThrowing: 异常通知
     */
    @AfterThrowing(value = "pointcut()", throwing = "e")
    public void afterReturningMethod(JoinPoint joinPoint, Exception e) {
        //logger.info("调用了异常通知");
    }

    /**
     * Around object
     *
     * @param pjp pjp
     * @return the object
     * @throws Throwable throwable
     * @Around：环绕通知
     */
    @Around("pointcut()")
    public Object around(ProceedingJoinPoint pjp) throws Throwable {
        //执行目标方法内容，获取执行结果
        Object result = pjp.proceed();
        return result;
    }

    /**
     * 获取方法入参变量名
     *
     * @param cls        触发的类
     * @param methodName 触发的方法名
     * @param include    需要打印的变量名
     * @return
     * @throws Exception
     */
    private Map<String, Object> getMethodParamNames(Class cls,
                                                    String methodName, String include) throws Exception {
        ClassPool pool = ClassPool.getDefault();
        pool.insertClassPath(new ClassClassPath(cls));
        CtMethod cm = pool.get(cls.getName()).getDeclaredMethod(methodName);
        LocalVariableAttribute attr = (LocalVariableAttribute) cm
                .getMethodInfo().getCodeAttribute()
                .getAttribute(LocalVariableAttribute.tag);

        if (attr == null) {
            throw new Exception("attr is null");
        } else {
            Map<String, Object> paramNames = new HashMap<>(16);
            int paramNamesLen = cm.getParameterTypes().length;
            int pos = Modifier.isStatic(cm.getModifiers()) ? 0 : 1;
            if (StringUtils.isEmpty(include)) {
                for (int i = 0; i < paramNamesLen; i++) {
                    paramNames.put(attr.variableName(i + pos), i);
                }
            } else { // 若include不为空
                for (int i = 0; i < paramNamesLen; i++) {
                    String paramName = attr.variableName(i + pos);
                    if (include.indexOf(paramName) > -1) {
                        paramNames.put(paramName, i);
                    }
                }
            }
            return paramNames;
        }
    }

    /**
     * 组装入参Map
     *
     * @param point            　　　　　　　切点
     * @param methodParamNames 　参数名称集合
     * @return
     */
    private Map getArgsMap(JoinPoint point,
                           Map<String, Object> methodParamNames) {
        Object[] args = point.getArgs();
        if (null == methodParamNames) {
            return Collections.EMPTY_MAP;
        }
        for (Map.Entry<String, Object> entry : methodParamNames.entrySet()) {
            int index = Integer.parseInt(String.valueOf(entry.getValue()));
            if (args != null && args.length > 0) {
                Object arg = (null == args[index] ? "" : args[index]);
                methodParamNames.put(entry.getKey(), arg);
            }
        }
        return methodParamNames;
    }

    /**
     * com.bwda.dsrs.systemservice.api.LogTemplate转换为com.bwda.dsrs.domain.po.serverslog.LogTemplate
     *
     * @param form
     * @return
     */
    private com.bwda.dsrs.domain.po.serverslog.LogTemplate getConditionByQueryForm(com.bwda.dsrs.systemservice.api.LogTemplate form) {
        com.bwda.dsrs.domain.po.serverslog.LogTemplate condition = CopyUtil.transfer(form, com.bwda.dsrs.domain.po.serverslog.LogTemplate.class);
        return condition;
    }
}
